#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

// [[ex03cc]]
// DEBUG descr The general hold written with the plain POSIX threads API.
class hold {
public:
	hold() : count_(0), waiting_(0)
	{ 
		pthread_mutex_init(&mutex_, NULL);
		pthread_cond_init(&cond_, NULL);
	}
	
	~hold()
	{
		pthread_mutex_destroy(&mutex_);
		pthread_cond_destroy(&cond_);
	}

	void acquire() 
	{
		pthread_mutex_lock(&mutex_);
		++count_;
		pthread_mutex_unlock(&mutex_);
	}

	// returns the number of holds left
	int release() 
	{
		int n;
		pthread_mutex_lock(&mutex_);
		if (--count_ <= 0 && waiting_ != 0)
			pthread_cond_broadcast(&cond_);
		n = count_;
		pthread_mutex_unlock(&mutex_);
		return n;
	}

	void wait()
	{
		pthread_mutex_lock(&mutex_);
		while(count_ != 0) {
			++waiting_;
			pthread_cond_wait(&cond_, &mutex_);
			--waiting_;
		}
		pthread_mutex_unlock(&mutex_);
	}

	int get_hold_count()
	{
		int ret;
		pthread_mutex_lock(&mutex_);
		ret = count_;
		pthread_mutex_unlock(&mutex_);
		return ret;
	}

protected:
	// access to count_
	pthread_mutex_t mutex_; 
	// signaled when count_ drops to 0
	pthread_cond_t cond_; 
	// count of holds
	int count_; 
	// count of threads waiting for release
	int waiting_; 
};
// END

// DEBUG {
#include "ptwrap.hpp"

hold hld;

class waiter : public pw::pwthread
{
public:
	// auto-start on construction and stop before destruction
	waiter()
	{
		start();
	}
	~waiter()
	{
		join();
	}
	void *execute()
	{
		hld.wait();
		fprintf(stderr, "wait succeeded\n");
		return 0;
	}
};

int main()
{
	hld.acquire();
	hld.acquire();
	fprintf(stderr, "hold count = %d\n", hld.get_hold_count());
	{ 
		waiter w;
		fprintf(stderr, "hold release = %d\n", hld.release());
		fprintf(stderr, "hold release = %d\n", hld.release());
	};

	return 0;
}
// DEBUG }
/* Sample output:

hold count = 2
hold release = 1
wait succeeded
hold release = 0

*/
